/**
 * Copyright (c) iwindplus Technologies Co., Ltd.2024-2030, All rights reserved.
 */

package com.iwindplus.setup.server.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import com.iwindplus.boot.domain.vo.ResultVO;
import com.iwindplus.boot.mail.domain.dto.MailDTO;
import com.iwindplus.boot.mail.domain.dto.MailDTO.Attachment;
import com.iwindplus.boot.mail.domain.property.MailProperty;
import com.iwindplus.boot.mail.domain.vo.MailVO;
import com.iwindplus.boot.mail.service.MailService;
import com.iwindplus.boot.sms.domain.constant.SmsConstant;
import com.iwindplus.boot.util.FilesUtil;
import com.iwindplus.boot.util.JacksonUtil;
import com.iwindplus.boot.util.TemplateUtil;
import com.iwindplus.log.client.MailCaptchaLogClient;
import com.iwindplus.log.client.MailLogClient;
import com.iwindplus.log.domain.dto.MailCaptchaLogDTO;
import com.iwindplus.log.domain.dto.MailLogDTO;
import com.iwindplus.log.domain.dto.MailSendValidDTO;
import com.iwindplus.mgt.client.power.UserClient;
import com.iwindplus.mgt.domain.dto.power.UserBaseQueryDTO;
import com.iwindplus.mgt.domain.vo.power.UserInfoVO;
import com.iwindplus.mgt.domain.vo.power.UserVO;
import com.iwindplus.setup.domain.vo.MailConfigVO;
import com.iwindplus.setup.domain.vo.MailTplVO;
import com.iwindplus.setup.server.model.MailConfigDO;
import com.iwindplus.setup.server.service.MailConfigService;
import com.iwindplus.setup.server.service.MailTplService;
import com.iwindplus.setup.server.service.MailboxService;
import jakarta.annotation.Resource;
import java.io.File;
import java.nio.charset.Charset;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

/**
 * 邮箱业务层接口实现类.
 *
 * @author zengdegui
 * @since 2021/7/20
 */
@Slf4j
@Service
@Transactional(rollbackFor = Exception.class)
public class MailboxServiceImpl implements MailboxService {

    @Resource
    private MailConfigService mailConfigService;

    @Resource
    private MailTplService mailTplService;

    @Resource
    private MailService mailService;

    @Resource
    private MailLogClient mailLogClient;

    @Resource
    private MailCaptchaLogClient mailCaptchaLogClient;

    @Resource
    private UserClient userClient;

    @Resource
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    @Override
    public void send(String code, String subject, String content, List<String> tos, List<String> ccs, List<String> bccs,
        List<MultipartFile> attachments) {
        MailConfigVO mailConfig = this.getMailConfig(code);
        MailConfigDO model = BeanUtil.copyProperties(mailConfig, MailConfigDO.class);
        List<Attachment> attachmentList = this.buildAttachments(attachments);

        this.sendMail(subject, content, tos, ccs, bccs, model, attachmentList);
    }

    @Override
    public void sendTwo(String code, String subject, String content, List<String> tos, List<String> ccs, List<String> bccs, List<File> attachments) {
        MailConfigVO mailConfig = this.getMailConfig(code);
        MailConfigDO model = BeanUtil.copyProperties(mailConfig, MailConfigDO.class);
        List<Attachment> attachmentList = this.buildAttachmentsTwo(attachments);

        this.sendMail(subject, content, tos, ccs, bccs, model, attachmentList);
    }

    @Override
    public void sendCaptcha(String tplCode, String mail) {
        final UserBaseQueryDTO entity = UserBaseQueryDTO.builder().mail(mail).build();
        final ResultVO<UserInfoVO> userResponse = this.userClient.getLoginInfoByCondition(entity);
        userResponse.errorThrow();
        final UserInfoVO user = userResponse.getBizData();
        final Long userId = user.getUserId();
        final Long orgId = user.getOrgId();

        this.sendCaptcha(tplCode, mail, userId, orgId);
    }

    @Override
    public void sendCaptchaByUserId(String tplCode, Long userId, Long orgId) {
        final ResultVO<UserVO> userResponse = this.userClient.getDetail(userId);
        userResponse.errorThrow();
        final UserVO user = userResponse.getBizData();
        final String mail = user.getMail();

        this.sendCaptcha(tplCode, mail, userId, orgId);
    }

    private void saveMailLog(MailConfigDO mailConfig, MailDTO entity, MailVO data) {
        // 保存日志
        MailLogDTO param = MailLogDTO.builder()
            .subject(entity.getSubject())
            .content(entity.getContent())
            .username(mailConfig.getUsername())
            .nickName(mailConfig.getNickName())
            .bizNumber(data.getBizNumber())
            .result(data.getResult())
            .sendCount(data.getSendCount())
            .errorMsg(data.getErrorMsg())
            .build();
        param.setTos(JacksonUtil.toJsonStr(entity.getTos()));
        if (CollUtil.isNotEmpty(entity.getCcs())) {
            param.setCcs(JacksonUtil.toJsonStr(entity.getCcs()));
        }
        if (CollUtil.isNotEmpty(entity.getBccs())) {
            param.setBccs(JacksonUtil.toJsonStr(entity.getBccs()));
        }
        this.mailLogClient.save(param);
    }

    private MailConfigVO getMailConfigById(Long configId) {
        MailConfigVO mailConfig = this.mailConfigService.getDetail(configId);
        this.buildMailConfig(mailConfig);
        return mailConfig;
    }

    private MailConfigVO getMailConfig(String code) {
        MailConfigVO mailConfig = this.mailConfigService.getByCode(code);
        this.buildMailConfig(mailConfig);
        return mailConfig;
    }

    private void buildMailConfig(MailConfigVO mailConfig) {
        MailProperty config = new MailProperty();
        config.setHost(mailConfig.getHost());
        config.setUsername(mailConfig.getUsername());
        config.setPassword(mailConfig.getPassword());
        config.setPort(mailConfig.getPort());
        config.setNickName(mailConfig.getNickName());
        config.setEnableRetry(mailConfig.getRetryEnable());
        config.setDefaultEncoding(Charset.defaultCharset());
        if (Boolean.TRUE.equals(mailConfig.getSslEnable())) {
            Map<String, String> props = new HashMap<>(16);
            props.put("mail.smtp.ssl.enable", "true");
            props.put("mail.smtp.ssl.trust", mailConfig.getHost());
            props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
            props.put("mail.smtp.auth", "true");
            props.put("mail.smtp.starttls.enable", "true");
            props.put("mail.smtp.starttls.required", "true");
            props.put("mail.mime.splitlongparameters", "false");
            config.setProperties(props);
        }
        this.mailService.setConfig(config);
    }

    private List<Attachment> buildAttachments(List<MultipartFile> attachments) {
        if (CollUtil.isEmpty(attachments)) {
            return null;
        }
        List<Attachment> mailAttachment = Lists.newArrayList();
        attachments.forEach(attachment -> {
            Attachment build = Attachment.builder()
                .sourceFileName(attachment.getOriginalFilename())
                .data(FilesUtil.getBytes(attachment))
                .build();
            mailAttachment.add(build);
        });
        return mailAttachment;
    }

    private List<Attachment> buildAttachmentsTwo(List<File> attachments) {
        if (CollUtil.isEmpty(attachments)) {
            return null;
        }
        List<Attachment> mailAttachment = Lists.newArrayList();
        attachments.forEach(attachment -> {
            Attachment build = Attachment.builder()
                .sourceFileName(attachment.getName())
                .data(FilesUtil.getBytes(attachment))
                .build();
            mailAttachment.add(build);
        });
        return mailAttachment;
    }

    private void sendMail(String subject, String content, List<String> tos, List<String> ccs, List<String> bccs, MailConfigDO model,
        List<Attachment> attachmentList) {
        MailDTO entity = MailDTO.builder()
            .subject(subject)
            .content(content)
            .tos(tos)
            .ccs(ccs)
            .bccs(bccs)
            .attachments(attachmentList)
            .build();
        CompletableFuture.runAsync(() -> {
            final MailVO data = this.mailService.send(entity);
            if (ObjectUtil.isNotEmpty(data) && data.getResult()) {
                this.saveMailLog(model, entity, data);
            }
            log.info("发送邮件失败={}", data);
        }, this.threadPoolTaskExecutor);
    }

    private void sendCaptcha(String tplCode, String mail, Long userId, Long orgId) {
        final MailTplVO mailTpl = this.mailTplService.getByCode(tplCode);
        MailConfigVO result = this.getMailConfigById(mailTpl.getConfigId());
        final MailSendValidDTO entity = MailSendValidDTO.builder()
            .tplCode(tplCode)
            .userId(userId)
            .orgId(orgId)
            .limitCountDay(mailTpl.getLimitCountDay())
            .limitCountHour(mailTpl.getLimitCountHour())
            .limitCountMinute(mailTpl.getLimitCountMinute())
            .build();
        final ResultVO<Boolean> canSend = this.mailCaptchaLogClient.checkCanSend(entity);
        canSend.errorThrow();

        MailDTO cond = new MailDTO();
        cond.setTos(Arrays.asList(mail));
        String captcha = RandomUtil.randomNumbers(6);
        Integer timeout = Objects.isNull(mailTpl.getCaptchaTimeout()) ? SmsConstant.CAPTCHA_TIMEOUT : mailTpl.getCaptchaTimeout();
        cond.setSubject(mailTpl.getName());
        List<String> templateParams = Arrays.asList(captcha, timeout.toString());
        String templateContent = TemplateUtil.getTemplateContent(mailTpl.getTemplateContent(), templateParams);
        cond.setContent(templateContent);

        CompletableFuture.runAsync(() -> {
            final MailVO data = this.mailService.send(cond);
            if (ObjectUtil.isNotEmpty(data) && data.getResult()) {
                LocalDateTime expireTime = LocalDateTime.now().plusMinutes(timeout);
                MailCaptchaLogDTO param = MailCaptchaLogDTO.builder()
                    .bizNumber(data.getBizNumber())
                    .tplCode(tplCode)
                    .mail(mail)
                    .captcha(captcha)
                    .expireTime(expireTime)
                    .userId(userId)
                    .orgId(orgId)
                    .build();
                this.mailCaptchaLogClient.save(param);
                return;
            }
            log.info("发送邮件失败={}", data);
        }, this.threadPoolTaskExecutor);
    }
}
